
#ifndef AFTERMATH_TEMPLATE_MATH_HPP_INCLUDED
#define AFTERMATH_TEMPLATE_MATH_HPP_INCLUDED

#include <cstddef>
//#include <cstdint>

namespace aftermath
{
    //template <typename t_target_type, typename t_source_type, t_source_type t_source>
    //struct checked_cast
    //{
    //    static const t_target_type unchecked_value = static_cast<t_target_type>(t_source);
    //    static const bool is_underflow = unchecked_value > t_source;
    //    static const bool is_overflow = unchecked_value < t_source;
    //    static const bool is_left_equal = t_source == unchecked_value;
    //    static const bool is_right_equal = unchecked_value == t_source;
    //    static const t_target_type value = unchecked_value;
    //};

    template <std::size_t t_number>
    struct is_power_of_two
    {
        static const bool value = ((t_number & (t_number - 1UL)) == 0);
    };

    template <std::size_t t_number>
    struct log_base_two
    {
        static const std::size_t value = 1UL + log_base_two<(t_number >> 1)>::value;
    };

    template <>
    struct log_base_two<2UL>
    {
        static const std::size_t value = 1UL;
    };

    template <>
    struct log_base_two<1UL>
    {
        static const std::size_t value = 0UL;
    };

    template <std::size_t t_base, std::size_t t_power>
    struct npow
    {
        static const std::size_t value = t_base * npow<t_base, t_power - 1UL>::value;
    };

    template <std::size_t t_base>
    struct npow<t_base, 1UL>
    {
        static const std::size_t value = t_base;
    };

    template <std::size_t t_base>
    struct npow<t_base, 0UL>
    {
        static const std::size_t value = 1UL;
    };

    template <std::size_t t_power>
    struct mersenne_number
    {
        static const std::size_t value = 2 * mersenne_number<t_power - 1UL>::value + 1UL;
    };

    template <>
    struct mersenne_number<1UL>
    {
        static const std::size_t value = 1UL;
    };

    template <>
    struct mersenne_number<0UL>
    {
        static const std::size_t value = 0UL;
    };

    template <std::size_t t_number, std::size_t t_count>
    struct falling_factorial
    {
        static const std::size_t value = t_number * falling_factorial<t_number - 1UL, t_count - 1UL>::value;
    };

    template <std::size_t t_number>
    struct falling_factorial<t_number, 1UL>
    {
        static const std::size_t value = t_number;
    };

    template <std::size_t t_number>
    struct falling_factorial<t_number, 0UL>
    {
        static const std::size_t value = 1UL;
    };

    template <std::size_t t_number, std::size_t t_count>
    struct factorial : public falling_factorial<t_number, t_number>
    {

    };

    template <std::size_t t_total, std::size_t t_choose>
    struct nchoosek
    {
        static const std::size_t value = (falling_factorial<t_total, t_choose>::value) / falling_factorial<t_choose, t_choose>::value;
    };

    template <std::size_t t_total>
    struct nchoosek<t_total, 0UL>
    {
        static const std::size_t value = 1UL;
    };

    template <std::size_t t_total>
    struct nchoosek<t_total, t_total>
    {
        static const std::size_t value = 1UL;
    };
}

#endif // AFTERMATH_TEMPLATE_MATH_HPP_INCLUDED
